Skip to content

Add send_keys_batch and clarify terminal workflow primitives#78

Merged
tony merged 17 commits into
mainfrom
feat/greenfield-terminal-ops
Jun 14, 2026
Merged

Add send_keys_batch and clarify terminal workflow primitives#78
tony merged 17 commits into
mainfrom
feat/greenfield-terminal-ops

Conversation

@tony

@tony tony commented Jun 13, 2026

Copy link
Copy Markdown
Member

Summary

  • Add send_keys_batch — sends an ordered batch of raw key/text operations to tmux panes and returns a typed per-operation result (index, resolved pane, success, error, elapsed). on_error selects stop-at-first-failure or continue-and-report, an optional timeout aborts a long-running batch, and every operation carries its own pane/window targeting.
  • Keep the batch homogeneous by design — it sends raw input only and does not compose send → wait → capture. Authored command completion stays with run_command; repeated observation stays with capture_since. This preserves clean per-operation error attribution instead of growing a workflow DSL inside one tool.
  • Harden audit redaction for batched payloads — nested operations[].keys are digested to a length + SHA-256 prefix exactly like top-level send_keys, and malformed or schema-violating operation payloads are redacted by shape before the audit log can persist a raw value from an invalid request.
  • Preserve recovery suggestions in batch errors — each per-operation error string now carries the suggestion hint from the underlying ToolError, so a client recovering from one failed operation gets the same guidance a single send_keys call would.
  • Reframe the I/O guidance around three primitives — docs, prompt recipes, and tool docstrings now route authored shell commands through run_command, raw TUI / persistent-shell input through send_keys / send_keys_batch, and repeated observation through capture_since, with wait_for_channel reserved for custom shell completion.

Changes by area

Tool surface

  • src/libtmux_mcp/tools/pane_tools/io.py: send_keys_batch runtime — ordered iteration, on_error handling, a cooperative timeout checked between operations, and suggestion-preserving error capture.
  • src/libtmux_mcp/models.py: SendKeysOperation (strict, extra="forbid"), SendKeysOperationResult, and SendKeysBatchResult.
  • src/libtmux_mcp/tools/pane_tools/__init__.py, server.py: register the tool at the mutating tier and refresh the server instructions.

Audit middleware

  • src/libtmux_mcp/middleware.py: digest nested operations[].keys; redact malformed, non-list, and unknown-field operation payloads by shape before they can be logged.

Prompts & docs

  • src/libtmux_mcp/prompts/recipes.py: rewrite run_and_wait to teach run_command instead of a manual send_keys + wait_for_channel + capture_pane sequence.
  • docs/: new send-keys-batch tool page; prompts, quickstart, recipes, gotchas, and topic guides reframed around the three primitives; Sphinx cross-reference roles fixed for the new tools.
  • README.md, CHANGES: workflow overview and unreleased changelog entry.

Tests

  • tests/test_pane_tools.py: ordering, stop/continue, empty rejection, unknown-field rejection, timeout abort, suggestion preservation, and docstring routing.
  • tests/test_middleware.py: redaction of well-formed, malformed, and nonschema batch payloads.
  • tests/test_prompts.py, tests/test_server.py: updated prompt template and server-instruction expectations.

Design decisions

  • Homogeneous batch, not a workflow DSL: send_keys_batch deliberately refuses heterogeneous send → wait → capture steps. Mixing operation kinds would blur per-operation error attribution and grow an ad-hoc orchestration language inside a single MCP tool; authored completion already has run_command and observation has capture_since.
  • Redaction runs before schema validation: the audit middleware summarizes raw arguments before fastmcp validates them, so it cannot assume well-formed SendKeysOperation dicts. It checks each field by type and redacts anything unexpected — a bare string, a non-dict list item, or an unknown key can never leak a payload into the log.
  • Cooperative timeout, not preemptive: timeout is evaluated between operations rather than interrupting an in-flight send_keys. tmux key delivery is effectively instantaneous, so the guard exists to bound a long batch and stop spending agent tokens — not to kill a single hung send.

Test plan

  • Full verification chain: uv run ruff check ., uv run ruff format ., uv run mypy ., uv run py.test --reruns 0, just build-docs
  • test_send_keys_batch_sends_operations_in_order — operations dispatch in submitted order with per-operation results
  • test_send_keys_batch_stops_after_operation_error / test_send_keys_batch_continues_after_operation_erroron_error semantics and stopped_at
  • test_send_keys_batch_rejects_empty_operations — empty batch raises an expected error
  • test_send_keys_operation_rejects_unknown_fieldsextra="forbid" rejects misnamed fields
  • Batch timeout abort and per-operation suggestion preservation
  • test_summarize_args_redacts_send_keys_batch_operations plus malformed / nonschema payload redaction
  • test_send_keys_docstring_routes_authored_commands_to_run_command — docstring routes authored commands to run_command

Closes #49.
Closes #61.
Refs #50.

@codecov-commenter

codecov-commenter commented Jun 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.95238% with 32 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.97%. Comparing base (cc6e755) to head (1b0a2ab).

Files with missing lines Patch % Lines
src/libtmux_mcp/tools/pane_tools/io.py 67.50% 20 Missing and 6 partials ⚠️
src/libtmux_mcp/middleware.py 90.62% 3 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #78      +/-   ##
==========================================
- Coverage   85.07%   84.97%   -0.10%     
==========================================
  Files          42       42              
  Lines        2881     3042     +161     
  Branches      385      412      +27     
==========================================
+ Hits         2451     2585     +134     
- Misses        322      339      +17     
- Partials      108      118      +10     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

tony added 5 commits June 13, 2026 16:28
why: Agents need ordered raw terminal input without turning command completion into a heterogeneous workflow DSL.
what:
- Add typed send_keys_batch operation and result models
- Register the tool as mutating open-world shell input
- Return per-operation success and failure metadata with stop/continue behavior
- Redact nested batch key payloads in audit summaries
- Route runtime prompt guidance toward run_command and capture_since boundaries
why: send_keys_batch needs explicit regression coverage for sequencing, errors, and client-facing metadata.
what:
- Add functional tests for ordered batch sends and stop/continue failures
- Cover nested audit redaction for batch operation payloads
- Update prompt and server instruction tests for run_command-first guidance
- Pin open-world registration and safety visibility for send_keys_batch
why: Agents and operators need clear boundaries between authored commands, raw input, and repeated observation.
what:
- Add the send_keys_batch tool page and navigation entries
- Reframe prompts, quickstart, recipes, gotchas, and tool docs around run_command and capture_since
- Keep wait_for_channel documented as the low-level custom-completion escape hatch
- Include new batch/result models in generated API documentation config
why: The project overview should match the command/observation architecture exposed by the tools.
what:
- List send_keys_batch in the pane tool catalog
- Add running and driving workflow summaries
- Clarify that raw input batching is separate from command completion and observation
why: The changelog should announce the user-visible raw input batching feature and updated workflow guidance.
what:
- Add an unreleased entry for send_keys_batch
- Explain the split between run_command, capture_since, and raw input batching
- Reference issues #49 and #61
@tony tony force-pushed the feat/greenfield-terminal-ops branch from 3c633b1 to df3e19c Compare June 13, 2026 21:30
tony added 6 commits June 13, 2026 16:56
why: Unknown keys in nested send_keys_batch operations were ignored by
Pydantic, so misspelled targets could fall back to the default pane.
what:
- Forbid extra fields on SendKeysOperation
- Add parametrized validation coverage for unsupported operation fields
why: Audit summaries are built before schema validation reports malformed
send_keys_batch payloads, so non-dict operation entries must not be
logged verbatim.
what:
- Replace malformed nested operation entries with type metadata
- Add parametrized audit redaction coverage for malformed entries
why: Audit summaries are built before schema validation rejects malformed
send_keys_batch inputs, so invalid operation shapes must not preserve raw
payload text.
what:
- Redact non-list operations payloads by shape
- Redact unknown or wrong-typed nested operation fields
- Cover malformed operation payloads with parametrized regression tests
why: Exceptions from internal operations carry useful recovery suggestions that were being discarded in the stringification process.
what:
- Check for suggestion attribute before stringifying ToolError and ExpectedToolError
- Append suggestion to error string if present to aid MCP clients
- Add test coverage for suggestion preservation
why: Prevent operations from hanging indefinitely and consuming agent tokens
what:
- Add cooperative timeout check to send_keys_batch iteration
- Abort batch execution when timeout is exceeded
- Add test coverage for batch timeouts
why: FastMCP documentation standards require {tooliconl} for inline references
what:
- Update send_keys_batch references in concepts.md and safety.md to use {tooliconl}
- Update wait_for_channel references in wait-for-channel.md to use {tooliconl}
- Retain plain backticks in headings where custom Sphinx roles cause TOC generator crashes
@tony

tony commented Jun 13, 2026

Copy link
Copy Markdown
Member Author

Code review

No issues found. Checked for bugs and AGENTS.md compliance.

🤖 Generated with Claude Code

tony added 4 commits June 13, 2026 18:42
why: The closing sentence narrated the docs/prompt-recipe reframing
and re-announced run_command, which shipped in 0.1.0a12 — both
restate change history the diff and commit log already carry.
what:
- Drop the "docs and prompt recipes now route ... rather than ..."
  sentence from the unreleased What's new entry
- Keep the send_keys_batch capability description and the
  run_command / capture_since scoping guidance
why: Malformed send_keys_batch schema errors can expose raw key
payloads before audit redaction runs.
what:
- Add a strict xfail regression through the production FastMCP server
- Assert validation result text and warning logs do not echo payloads
why: FastMCP logs and tool error results render Pydantic
validation inputs before send_keys_batch audit redaction can run.
what:
- Format schema validation errors without rejected input values
- Redact FastMCP invalid-argument log records before handlers render
- Convert the strict xfail payload-leak case to a passing regression
why: send_keys_batch only checks the batch deadline before an
operation starts, so a stalled send can exceed timeout and still report
success.
what:
- Add a strict xfail regression for a single blocked send operation
- Keep the mocked subprocess timeout dormant until the timed send path exists
@tony

tony commented Jun 14, 2026

Copy link
Copy Markdown
Member Author

Code review

Re-reviewed the commits added since the previous review: the send_keys_batch timeout parameter, error-suggestion preservation, and the audit fix that redacts schema-validation inputs from FastMCP logs and tool error results before they render. The redaction was verified to close the leak on both paths (_format_schema_validation_error and the fastmcp.server.server log filter), with the strict-xfail reproduction promoted to a passing regression. No issues found. Checked for bugs and AGENTS.md compliance.

🤖 Generated with Claude Code

tony added 2 commits June 13, 2026 19:58
why: Timed batches must bound the tmux send operation itself, not
just skip operations whose start time is already past the batch deadline.
what:
- Send timed batch operations through subprocess.run with remaining timeout
- Keep untimed sends on the existing libtmux send_keys path
- Convert the strict xfail timeout regression into a passing test
why: The unreleased notes predated the batch error-handling and
timeout affordances and the argument-validation input redaction,
all of which are user-facing.
what:
- Note send_keys_batch stop/continue handling and the optional
  timeout in the existing What's new entry
- Add a Fixes entry: argument-validation failures no longer echo
  rejected input into logs or error text
@tony tony merged commit 31e5a03 into main Jun 14, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants